package com.bluesky.jwf;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import org.apache.log4j.Logger;

import com.bluesky.javawebbrowser.domain.html.HtmlPage;
import com.bluesky.javawebbrowser.domain.html.tags.Tag;
import com.bluesky.jwf.component.Component;
import com.bluesky.jwf.config.ComponentInfo;
import com.bluesky.jwf.config.ComponentRegistry;
import com.bluesky.jwf.config.TemplateResourceInfo;

public class ComponentFactory {

	public static enum ComponentType {
		TextField, LongTextField, FileField, CheckBox, DropDownList, ListBox, Captcha, Button, HyperLinkButton, Label, Image, DataTable, ListView, PaginationListView, TreeView, Tabs, Window, Dialog;
	}

	private static Logger logger = Logger.getLogger(ComponentFactory.class);

	private static ComponentFactory instance;
	private static String configFileName;
	private static String charEncoding;
	private Long nextId;

	public static String getConfigFileName() {
		return configFileName;
	}

	public static void setConfigFileName(String configFileName) {
		ComponentFactory.configFileName = configFileName;
	}

	public static String getCharEncoding() {
		return charEncoding;
	}

	public static void setCharEncoding(String charEncoding) {
		ComponentFactory.charEncoding = charEncoding;
	}

	private ComponentFactory() {

		registry = new ComponentRegistry();

		InputStream is = registry.getClass()
				.getResourceAsStream(configFileName);

		if (is == null) {
			logger.info("can not found config file:" + configFileName);
			throw new RuntimeException("can not found config file:"
					+ configFileName);
		}

		registry.load(is);

		nextId = Long.valueOf(0);
	}

	public static ComponentFactory getInstance() {
		if (instance == null)
			instance = new ComponentFactory();

		return instance;
	}

	private ComponentRegistry registry;

	public Component createComponent(ComponentType type) {
		return createComponent(type.toString());
	}

	public Component createComponent(String className) {
		return createComponent(className, null);
	}

	/**
	 * 
	 * @param clazz
	 * @return
	 */
	public Component createComponent(String className, String templateStr) {
		ComponentInfo info;
		if (className.indexOf('.') < 0) {// it is an alias
			info = registry.getComponentInfo(className);
			if (info == null)
				throw new RuntimeException("unknow alias:" + className);
		} else {
			info = new ComponentInfo();
			info.setClassName(className);
		}

		Class clazz;
		try {
			clazz = Class.forName(info.getClassName());
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);

		}

		Component comp;
		try {
			comp = (Component) clazz.newInstance();
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}

		if (templateStr == null) {
			Template temp = getTemplate(clazz, info.getTemplateResourceInfo());
			templateStr = temp.getTemplate();

			comp.setAttribute(Tag.JWF_ID, temp.getJwfId());
		}
		// template resource can be not existing
		if (!templateStr.isEmpty())
			comp.init(templateStr);

		comp.setId(String.format("%s%d", className, nextId));
		nextId++;

		return comp;

	}

	static private Template getTemplate(Class clazz, TemplateResourceInfo info) {
		// ...checks on aFile are elided
		StringBuilder contents = new StringBuilder();

		try {
			// use buffering, reading one line at a time
			// FileReader always assumes default encoding is OK!
			InputStream is;
			String fileName;
			if (info.getFileName() == null || info.getFileName().equals(""))
				fileName = clazz.getSimpleName() + ".html";
			else
				fileName = info.getFileName();

			is = clazz.getResourceAsStream(fileName);

			if (is == null)
				throw new RuntimeException("file do not exist:" + fileName);

			Reader reader = new InputStreamReader(is, charEncoding);

			BufferedReader input = new BufferedReader(reader);
			try {
				String line = null; // not declared within while loop
				/*
				 * readLine is a bit quirky : it returns the content of a line
				 * MINUS the newline. it returns null only for the END of the
				 * stream. it returns an empty String if two newlines appear in
				 * a row.
				 */
				while ((line = input.readLine()) != null) {
					contents.append(line);
					contents.append(System.getProperty("line.separator"));
				}
			} finally {
				input.close();
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		}

		HtmlPage page = new HtmlPage(contents.toString(), true);
		// System.out.println("*************Tag Tree***************");
		// System.out.println(page.getRoot().toTagTree());
		String id;
		if (info.getId() != null)
			id = info.getId();
		else
			id = clazz.getSimpleName() + "Container";

		Template temp = new Template();
		temp.setJwfId(id);
		if (id.equals("ALL"))
			temp.setTemplate(contents.toString());
		else {
			Tag root = page.getRoot().loadByJwfId(id);

			if (root.getJwfInclude())
				temp.setTemplate(root.toHtml());
			else
				temp.setTemplate(root.descendantToHtml());

		}

		return temp;
	}
}
